﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Morus.Presentation
{
    public class CompositePresenterDiscoveryStrategy : IPresenterDiscoveryStrategy
    {
        private readonly IEnumerable<IPresenterDiscoveryStrategy> _strategies;
        private readonly IEqualityComparer<IEnumerable<IView>> _viewInstanceListComparer = new TypeListComparer<IView>();

        public CompositePresenterDiscoveryStrategy()
            : this(PresenterDiscoveryStrategies.Strategies)
        {
        }

        public CompositePresenterDiscoveryStrategy(params IPresenterDiscoveryStrategy[] strategies)
            : this((IEnumerable<IPresenterDiscoveryStrategy>)strategies)
        {
        }

        public CompositePresenterDiscoveryStrategy(IEnumerable<IPresenterDiscoveryStrategy> strategies)
        {
            Check.Argument.IsNotNull(strategies, "strategies");

            _strategies = strategies.ToArray();

            if (!strategies.Any())
            {
                throw new ArgumentException("You must supply at least one strategy.", "strategies");
            }
        }

        public IEnumerable<PresenterDiscoveryResult> GetBindings(IEnumerable<object> hosts, IEnumerable<IView> viewInstances)
        {
            var results = new List<PresenterDiscoveryResult>();

            var pendingViewInstances = viewInstances;
            foreach (var strategy in _strategies)
            {
                if (!pendingViewInstances.Any())
                {
                    break;
                }

                var resultsThisRound = strategy.GetBindings(hosts, pendingViewInstances);

                results.AddRange(resultsThisRound);

                var viewsBoundThisRound = resultsThisRound
                    .Where(r => r.Bindings.Any())
                    .SelectMany(b => b.ViewInstances)
                    .Distinct();

                pendingViewInstances = pendingViewInstances.Except(viewsBoundThisRound);
            }

            return results.GroupBy(r => r.ViewInstances, _viewInstanceListComparer).Select(r => BuildMergedResult(r.Key, r));
        }

        private static PresenterDiscoveryResult BuildMergedResult(IEnumerable<IView> viewInstances, IEnumerable<PresenterDiscoveryResult> results)
        {
            return new PresenterDiscoveryResult
            (
                viewInstances, "CompositePresenterDiscoveryStrategy:\r\n\r\n{0}".FormatWith(string.Join("\r\n\r\n", results.Select(r => r.Message).ToArray())),
                results.SelectMany(r => r.Bindings)
            );
        }
    }
}